package com.mihuo.config.filter;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.mihuo.config.FilterConfig;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.web.util.ContentCachingResponseWrapper; // 引入响应包装器

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * 请求日志记录过滤器
 * 用于统一记录HTTP请求和响应的详细信息，包括请求参数、响应状态、处理时间和链路追踪
 *
 * @author mihuo
 * @version 1.0
 * @since 2025/09/30
 */
@Slf4j
public class LogFilter implements Filter {

    private static final String TRACE_ID_KEY = "traceId";
    private static final int MAX_PAYLOAD_LENGTH = 1024; // 定义响应体最大记录长度
    // 硬编码需要排除的路径列表[5](@ref)
    private static final List<String> EXCLUDED_PATHS = Arrays.asList(
            "/static/","/images/","/css/","/js/",
            ".html",".png",".jpg",".jpeg",".bmp",".mp4",".gif"
    );

//    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        log.info("LoggingFilter initialized");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        // 包装原始请求，以便缓存请求体，支持多次读取
        HttpServletRequest httpRequest = (HttpServletRequest) request;

        // 检查请求路径是否以任何排除路径开头
        boolean isExcluded = EXCLUDED_PATHS.stream().anyMatch(item -> httpRequest.getRequestURI().toLowerCase().startsWith(item) ||
                httpRequest.getRequestURI().toLowerCase().endsWith(item));
        if (isExcluded) {
            // 直接放行，不记录日志
            chain.doFilter(request, response);
            return;
        }

        String requestURI = httpRequest.getRequestURL().toString();

        CachedBodyHttpServletRequest wrappedRequest = new CachedBodyHttpServletRequest(httpRequest);

        // 包装原始响应，以便缓存响应体，支持多次读取
        ContentCachingResponseWrapper wrappedResponse = new ContentCachingResponseWrapper((HttpServletResponse) response);

        long startTime = System.currentTimeMillis();
        String traceId = generateAndSetTraceId(httpRequest); // 生成 traceId

        String clientIP = httpRequest.getRemoteAddr();
        String method = httpRequest.getMethod();
        String queryString = httpRequest.getQueryString();

        // 获取URL查询参数（针对GET请求或URL带参的请求）或请求体
        String requestBody = wrappedRequest.getCacheBodyJsonStr();
        queryString = StrUtil.isEmpty(queryString) ? requestBody : queryString + (StrUtil.isNotBlank(requestBody) ?  " " + requestBody : "");

        // 记录请求开始日志
        log.info("请求开始 - traceId: {} | {} {} | ip: {} | params: {}",
                traceId, method, requestURI, clientIP, queryString);

        // 继续处理请求（传递包装后的请求和响应）
        chain.doFilter(wrappedRequest, wrappedResponse);

        // --- 请求处理后，获取并记录响应信息和处理时间 ---
        long duration = System.currentTimeMillis() - startTime;

        // 获取响应状态
        int status = wrappedResponse.getStatus();
        // 获取响应体内容
        String responseBody = getResponseBody(wrappedResponse);

        // 记录请求完成信息，包括响应状态和响应体
        log.info("请求结束 - traceId: {} | Status: {} | Res time: {} ms | Response: {}",
                traceId, status, duration, responseBody);

        // 重要：将缓存的响应数据拷贝到原始的ServletResponse中，确保客户端能收到响应
        wrappedResponse.copyBodyToResponse();
    }

    @Override
    public void destroy() {
        log.info("LoggingFilter destroyed");
    }

    /**
     * 生成并设置 TraceId
     */
    private String generateAndSetTraceId(HttpServletRequest request) {
        String traceId = request.getHeader(TRACE_ID_KEY);
        if (traceId == null || traceId.isEmpty()) {
            traceId = IdUtil.fastSimpleUUID();
        }
        MDC.put(TRACE_ID_KEY, traceId);
        return traceId;
    }

    /**
     * 从包装的响应中获取响应体内容
     */
    private String getResponseBody(ContentCachingResponseWrapper response) {
        byte[] buf = response.getContentAsByteArray();
        if (buf.length > 0) {
            int length = Math.min(buf.length, MAX_PAYLOAD_LENGTH);
            try {
                return new String(buf, 0, length, StandardCharsets.UTF_8);
            } catch (Exception e) {
                return "[unknown]";
            }
        }
        return "[Empty Response]";
    }

    /**
     * 获取请求头信息 (可选，根据是否需要记录请求头决定)
     */
    private Map<String, String> getRequestHeaders(HttpServletRequest request) {
        Map<String, String> headersMap = new HashMap<>();
        Enumeration<String> headerNames = request.getHeaderNames();
        if (headerNames != null) {
            while (headerNames.hasMoreElements()) {
                String headerName = headerNames.nextElement();
                headersMap.put(headerName, request.getHeader(headerName));
            }
        }
        return headersMap;
    }
}